﻿/*
 *  Part of PPather
 *  Copyright Pontus Borg 2008
 *  
 * edited by Dante020
 * 
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Drawing;
using blp2decode;

namespace StormDll
{
    unsafe class StormDll
    {
        [DllImport("StormLib.dll")]
        public static extern uint SFileGetLocale();

        [DllImport("StormLib.dll")]
        public static extern bool SFileOpenArchive([MarshalAs(UnmanagedType.LPStr)]string szMpqName,
                              uint dwPriority, uint dwFlags,
                              void** phMpq);

        [DllImport("StormLib.dll")]
        public static extern bool SFileCloseArchive(void* hMpq);


        [DllImport("StormLib.dll")]
        public static extern bool SFileOpenFileEx(void* hMpq,
                                [MarshalAs(UnmanagedType.LPStr)] string szFileName,
                                uint dwSearchScope,
                                void** phFile);

        [DllImport("StormLib.dll")]
        public static extern bool SFileCloseFile(void* hFile);

        [DllImport("StormLib.dll")]
        public static extern uint SFileGetFilePos(void* hFile, uint* pdwFilePosHigh);

        [DllImport("StormLib.dll")]
        public static extern uint SFileGetFileSize(void* hFile, uint* pdwFileSizeHigh);

        [DllImport("StormLib.dll")]
        public static extern uint SFileSetFilePointer(void* hFile,
                    int lFilePos, int* pdwFilePosHigh, uint dwMethod);

        [DllImport("StormLib.dll")]
        public static extern bool SFileReadFile(void* hFile, void* lpBuffer, uint dwToRead,
                           uint* pdwRead, void* lpOverlapped);

        [DllImport("StormLib.dll")]
        public static extern bool SFileExtractFile(void* hMpq,
                    [MarshalAs(UnmanagedType.LPStr)] string szToExtract,
                    [MarshalAs(UnmanagedType.LPStr)] string szExtracted);

        [DllImport("StormLib.dll")]
        public static extern bool SFileHasFile(void* hMpq,
                    [MarshalAs(UnmanagedType.LPStr)] string szFileName);

        public static uint GetLocale()
        {
            return StormDll.SFileGetLocale();
        }
    }

    public unsafe class ArchiveSet
    {
        private List<Archive> archives = new List<Archive>();
        private string GameDir = ".\\";
        public void SetGameDir(string dir)
        {
            GameDir = dir;
        }
        public string SetGameDirFromReg()
        {
            RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Blizzard Entertainment\\World of Warcraft");
            if (key == null) return null;
            Object val = key.GetValue("InstallPath");
            if (val == null) return null;
            string s = val.ToString();
            SetGameDir(s + "Data\\");
            return s;
        }

        public bool AddArchive(string file)
        {
            Archive a = new Archive(GameDir + file, 0, 0);
            if (a.IsOpen())
            {
                archives.Add(a);
                System.Console.WriteLine("Add archive " + file);
                return true;
            }
            return false;
        }
        public int AddArchives(string[] files)
        {
            int n = 0;
            foreach (string s in files)
            {
                if (AddArchive(s)) n++;
            }
            return n;
        }

        public bool HasFile(string name)
        {
            foreach (Archive a in archives)
            {
                if (a.HasFile(name)) return true;
            }
            return false;
        }

        public File OpenFile(string file)
        {
            foreach (Archive a in archives)
            {
                if (a.HasFile(file))
                    return a.OpenFile(file, 0);
            }
            return null;
        }

        public bool ExtractFile(string from, string to)
        {
            foreach (Archive a in archives)
            {
                if (a.HasFile(from))
                {
                    return a.ExtractFile(from, to);
                }
            }
            return false;
        }
        public void Close()
        {
            foreach (Archive a in archives)
            {
                a.Close();
            }
            archives.Clear();
        }
    }

    public unsafe class Archive
    {
        private void* handle = null;

        public Archive(string file, uint Prio, uint Flags)
        {
            bool r = Open(file, Prio, Flags);
        }
        public bool IsOpen()
        {
            return handle != null;
        }

        private bool Open(string file, uint Prio, uint Flags)
        {
            void* h;
            void** hp = &h;
            bool r = StormDll.SFileOpenArchive(file, Prio, Flags, hp);
            handle = h;
            return r;
        }

        public bool Close()
        {
            bool r = StormDll.SFileCloseArchive(handle);
            if (r) handle = null;
            return r;
        }

        public File OpenFile(string szFileName, uint dwSearchScope)
        {
            void* h;
            void** hp = &h;
            bool r = StormDll.SFileOpenFileEx(handle, szFileName, dwSearchScope, hp);
            if (!r)
                return null;
            return new File(this, h);
        }

        public bool HasFile(string name)
        {
            bool r = StormDll.SFileHasFile(handle, name);
            return r;
        }

        public bool ExtractFile(string from, string to)
        {
            bool r = StormDll.SFileExtractFile(handle, from, to);
            return r;
        }
    }

    public unsafe class File
    {
        void* handle;
        Archive archive;
        public File(Archive a, void* h)
        {
            archive = a;
            handle = h;
        }

        public bool Close()
        {
            bool r = StormDll.SFileCloseFile(handle);
            if (r) handle = null;
            return r;
        }

        public uint GetSize()
        {
            uint high;
            uint* phigh = &high;
            uint low = StormDll.SFileGetFileSize(handle, phigh);
            return low;
        }

        public bool Read()
        {
            return false;
        }

        public Bitmap Blp2Bmp(string to)
        {
            uint uSize = this.GetSize();

            void* lpBuffer = (void*)Marshal.AllocHGlobal((int)uSize);
            uint* lpNumberOfBytesRead = (uint*)Marshal.AllocHGlobal((int)2);

            uint nNumberOfBytesToRead = uSize;

            Bitmap bmp = null;

            bool success = StormDll.SFileReadFile(
                handle,                 //Target file handle
                lpBuffer,               //Read to
                nNumberOfBytesToRead,   //Bytes to read
                lpNumberOfBytesRead,    //Bytes read
                null);                  //? - null

            if (!success)
            {
                Console.WriteLine("Error reading file.");
                return bmp;                
            }

            *lpNumberOfBytesRead = 0;
            bmp = decode.Blp2Bmp(lpBuffer,uSize,lpNumberOfBytesRead);

            //Deallocate memory
            Marshal.FreeHGlobal((IntPtr)lpBuffer);
            Marshal.FreeHGlobal((IntPtr)lpNumberOfBytesRead);

            bmp.Save(to);

            return bmp;
        }
    }
}